using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Html;
using System.Runtime.CompilerServices;
using Nsb.Classes;
using ThemeChain;
using WhirliGig.Classes;
using jQueryApi;

[Mixin("jQuery.fn")]
// ReSharper disable InconsistentNaming
public static class jQueryJGigExtensions
// ReSharper restore InconsistentNaming
{
    public static jQueryObject PopMenu(Dictionary opt)
    {
        GigMu gmu = new GigMu();
        gmu.Pdm = opt;

        ThemeYoshi.EscapeOut = false;
        jQueryObject btn = jQuery.Current;
        jQueryObject tabBox = btn.Plugin<JGig>().CreateMenu("TabBox");
        Dictionary dict = (Dictionary) opt["template"];
        string className = (string) (opt["class"] ?? "GigMu");
        tabBox.Data("className", "." + className);
        if (opt["host"] != null)
        {
            gmu.Host = (IFocusable) opt["host"];
            gmu.Host.Gmu = gmu;
        }

        string[] menus = (string[]) opt["tabs"];

        foreach (string n in menus)
        {
            tabBox.Plugin<JGig>().AddTab(gmu, n, (List<object>) dict[n]);
        }

        gmu.Glass = Cluster.Glass(
            delegate
            {
                jQuery.Document.Unbind("keydown.escGears");
                jQueryObject el2 = tabBox.Closest("." + className);
                btn.Plugin<JGig>().RemoveMenu(tabBox);
                tabBox.Remove();
                gmu.Pdm = null; // Make garbage collection easier
                if (gmu.Host != null)
                {
                    gmu.Host.Gmu = null;
                    gmu.Host = null;
                }
                if (el2.Length > 0)
                    el2.Remove();
                if (opt["Complete"] != null)
                {
                    Action cpt = (Action)opt["Complete"];
                    cpt();
                }
            });
        jQuery.Document
            .Unbind("keydown.escGears")
            .Bind("keydown.escGears",
                  delegate(jQueryEvent e)
                  {
                      if (e.Which == 27)
                      {
                          ThemeYoshi.EscapeOut = true;
                          DomElement.CancelEvent(e);
                          gmu.Glass.Trigger("mousedown");
                      }
                  })
            ;

        jQueryObject el = jQuery.FromHtml("<div class='MenuZee'/>").AddClass(className).AppendTo(Document.Body);
        gmu.Top = el;
        tabBox.AppendTo(el).Show().Plugin<JGig>().ResetTabs();
        tabBox.Find(".Tab").First().Trigger("mouseover");
        jQuery.FromHtml("<div class='GigMuDragBar'/>").AppendTo(el);
        el.Plugin<Jewl>().Draggable(new Dictionary("handle", ".GigMuDragBar"));
        return jQuery.Current;
    }

    public static jQueryObject CreateMenu(string className)
    {
        jQueryObject mu = jQuery.FromHtml("<div class='GearBox'/>").AddClass(className);
        jQuery.FromHtml("<div class='Tabs'/>").AppendTo(mu);
        jQuery.FromHtml("<div class='Pads'/>").AppendTo(mu);
        mu.Data("popBtn", jQuery.Current);
        return mu;
    }

    public static jQueryObject RemoveMenu(jQueryObject mu)
    {
        mu.Empty();
        mu.RemoveData("className");
        mu.RemoveData("popBtn");
        return jQuery.Current;
    }

    public static jQueryObject AddTab(GigMu gmu, string name, object padDef)
    {
        string pid = Formulas.NewId;
        jQueryEventHandler tabFn = null;
        List<GigMuPie> plst = null;
        if (jQuery.IsFunction(padDef))
            tabFn = (jQueryEventHandler) padDef;
        else if (jQuery.IsArray(padDef))
            plst = (List<GigMuPie>) padDef;
        else
            plst = (List<GigMuPie>) (object) new DList().Add(padDef).End();

        jQueryObject tabBox = jQuery.Current;
        jQueryObject tabs = tabBox.Find(".Tabs");
        jQueryObject pads = tabBox.Find(".Pads");
        string align = plst != null ? plst[0].align ?? "" : "";

        jQueryObject tab = jQuery.FromHtml("<div class='Tab'/>");
        tab
            .AppendTo(tabs)
            .Html(name.Split(":")[0])
            .Bind("mouseenter.Tab",
                  delegate
                  {
                      jQueryObject el = tabBox.Closest((string) tabBox.GetDataValue("className"));
                      tabs.Children().RemoveClass("Hover");
                      pads.Children().Hide();
                      if (tabFn == null)
                      {
                          jQueryObject thisPad = pads.Find("#" + pid);
                          el
                              .CSS(new Dictionary(
                                       "width", 400,
                                       "height", 250
                                       ));
                          thisPad.Show();
                          pads
                              .CSS(new Dictionary(
                                       "margin-top", align == "tab" ? tab.Position().Top : 0
                                       ));
                      }
                      el
                          .CSS(new Dictionary(
                                   "width", tabBox.GetOuterWidth(),
                                   "height", tabBox.GetOuterHeight()
                                   ));
                      tab.AddClass("Hover");
                  }
            )
            ;

        if (tabFn != null)
        {
            tab
                .Plugin<Jewl>().SwallowClick(tabFn)
                ;
        }
        else
        {
            Debug.Assert(plst != null, "plst != null");
            jQueryObject pad = jQuery.FromHtml("<div class='Pad'/>")
                .Hide()
                .Attribute("id", pid)
                .AppendTo(pads)
                ;
            jQueryEventHandler seter = null;
            Dictionary css = null;
            Dictionary tst = new Dictionary();
            string prop = null;
            // ReSharper disable PossibleNullReferenceException
            foreach (GigMuPie gmp in plst)
                // ReSharper restore PossibleNullReferenceException
            {
                string[] nnames = (gmp.html).Split("::");
                object value = gmp.value != null ? gmp.value : null; // Don't use ?? because must detect false as a value
                if (value == null)
                    value = nnames.Length > 1 ? nnames[1] : nnames[0];
                gmp.value = value;
                seter = (jQueryEventHandler) JGigHelpers.Prp(gmp, "set", seter);
                css = (Dictionary) JGigHelpers.Prp(gmp, "css", css);
                prop = (string) JGigHelpers.Prp(gmp, "prop", prop);
                gmp.prop = prop;
                jQueryObject pie = jQuery.FromHtml("<div class='Pie'/>");
                pie
                    .AppendTo(pad)
                    .Html(nnames[0])
                    .Click(Helpers.ClosureEv(
                        delegate(jQueryEvent e, jQueryEventHandler s1, object pi, object dd)
                        {
                            GigMuPie gmp2 = (GigMuPie) dd;
                            if (gmu.Host != null)
                            {
                                gmu.Pdp = gmp2;
                                gmu.Pad = pad;
                                gmu.Pie = (jQueryObject) pi;
                                gmu.Pel = jQuery.FromElement(e.Target);
                                gmu.Host.FillGmu();
                                gmu.Host.SetSaved(false);
                            }
                            if (gmp2.prop != null)
                            {
                                pad.Find(".Pie." + gmp2.prop).RemoveClass("Selected");
                                ((jQueryObject) pi).AddClass("Selected");
                            }
                            if (s1 != null)
                                s1(e);
                        }, seter, pie, gmp)
                    )
                    //.MouseDown(delegate(jQueryEvent e) { e.PreventDefault(); }) // Cancels shift-select-text on the page.
                    .MouseDown(delegate(jQueryEvent e) { if (!jQuery.FromElement(e.Target).Is("select")) DomElement.CancelEvent(e); }) // Cancels shift-select-text on the page.
                    .MouseUp(DomElement.CancelEvent) // Cancels shift-select-text on the page.
                    .Data("value", value)
                    ;

                gmu.Pdp = gmp;
                gmu.Pad = pad;
                gmu.Pie = pie;
                gmu.Pel = null;

                if (gmu.Host != null)
                    gmu.Host.FillGmu();

                if (prop != null)
                    pie.AddClass(prop);

                if (css != null)
                    pie.CSS(css);

                if (gmp.brk)
                    jQuery.FromHtml("<div class='Break'/>").AppendTo(pad);

                if (gmp.hr)
                    jQuery.FromHtml("<hr/>").AppendTo(pad);

                if (gmu.Host != null)
                {
                    tst["item"] = gmp;
                    tst["value"] = value;
                    gmu.Host.MarkItem(pie, tst);
                }
                if (gmp.build != null)
                    gmp.build(gmu);
            }
        }


        return tabBox;
    }

    public static jQueryObject ResetTabs()
    {
        jQueryObject ths = jQuery.Current;
        jQueryObject tabs = ths.Find(".Tabs");
        jQueryObject pads = ths.Find(".Pads");
        jQueryObject el = ths.Closest((string) ths.GetDataValue("className"));
        tabs.Children().RemoveClass("Hover");
        pads.Children().Hide();
        el.Plugin<Jewl>().Position(new Dictionary("my", "left top", "at", "left top", "of", (jQueryObject) ths.GetDataValue("popBtn"), "offset", "-5 -5"));
        el
            .CSS(new Dictionary(
                     "width", ths.GetOuterWidth(),
                     "height", ths.GetOuterHeight()
                     ));
        jQueryPosition p = el.GetOffset();
        const int w = 400;
        const int h = 300;
        if (p.Left - Document.Body.ScrollLeft + w > Window.Self.InnerWidth)
            el.CSS(new Dictionary("left", Window.Self.InnerWidth + Document.Body.ScrollLeft - w));
        if (p.Top - Document.Body.ScrollHeight + h > Window.Self.InnerHeight)
            el.CSS(new Dictionary("top", Window.Self.InnerHeight + Document.Body.ScrollHeight - h));
        return ths;
    }

    // Expirimental - has problem with SelectTo element offset.
    public static jQueryObject PositionAt(jQueryObject node)
    {
        jQueryPosition p = node.GetOffset();
        jQueryPosition x = node.Position();
        jQuery.Current.CSS(new Dictionary("top", p.Top, "left", x.Left));
        Inform.Debug("o.top {0}, o.left {1}, p.top {2}, p.left {3}", p.Top, p.Left, x.Top, x.Left);
        return jQuery.Current;
    }
}

[Imported]
public sealed class JGig : jQueryObject
{
    public jQueryObject PopMenu(Dictionary opt)
    {
        return null;
    }

    public jQueryObject CreateMenu(string className)
    {
        return null;
    }

    public jQueryObject RemoveMenu(jQueryObject menu)
    {
        return null;
    }

    [AlternateSignature]
    public extern jQueryObject AddTab(GigMu gmu, string name, jQueryEventHandler fn);

    [AlternateSignature]
    public extern jQueryObject AddTab(GigMu gmu, string name, Dictionary pad);

    public jQueryObject AddTab(GigMu gmu, string name, List<object> pad)
    {
        return null;
    }

    public jQueryObject ResetTabs()
    {
        return null;
    }

    public jQueryObject PositionAt(jQueryObject node)
    {
        return null;
    }
}

namespace WhirliGig.Classes
{
    public static class JGigHelpers
    {
        public static object Prp(object gmp, string n, object v)
        {
            Dictionary d = (Dictionary) gmp;
            if ((bool) Script.Literal("{0}===false", d[n]))
                return null;
            return d[n] ?? v;
        }
    }


    public interface IFocusable
    {
        GigMu Gmu { get; set; }
        void MarkItem(jQueryObject pie, Dictionary tst);
        void FillGmu();
        void SetSaved(bool ic);
    }

    public sealed class GigMu
    {
        public Dictionary Css; // The gig css
        public jQueryObject Glass; // Glass behind the menu
        public IFocusable Host; // Owner of this menu
        public Action<jQueryObject, Dictionary> MarkItem;
        public jQueryObject Pad; // The current pad (the tab body)
        public Dictionary Pdm; // Top level menu dictionary passed to constructor
        public GigMuPie Pdp; // Dictionary for current pie
        public jQueryObject Pel; // Element clicked on
        public jQueryObject Pie; // Clickable thing on a pad
        public jQueryObject Top; // Top element of the displayed menu box
    }

    // ReSharper disable UnassignedField.Global
    // ReSharper disable InconsistentNaming
    public sealed class GigMuPie
    {
        public string align;
        public bool brk;
        public Action<GigMu> build;
        //public string className;
        public Dictionary css;
        public bool hr;
        public string html;
        public string prop;
        public jQueryEventHandler set;
        public object value;
    }
}